package kGeom;


public class kVecField extends kElement {
	private kVec[]	m_vecs;
	public kRange	range;
	private int	m_res;
	public float vecDrawScale = 1.0f;
	public kVec dontDrawVec = new kVec(0,0,0);
	
	public kVecField(kRange rng, int res){
		range = rng;
		m_res = res;
		
		m_vecs = new kVec[res*res*res];
		for (int n = 0; n < m_vecs.length; n++) {
			m_vecs[n] = new kVec(0,-1,0);
			m_vecs[n].normalize();
		}
	}
	

	
	public int res(){ return m_res;}
	
	public kVec getAt(int u, int v, int w){
		if (u < m_res && v < m_res && w < m_res ){return kVec.clone(m_vecs[(w*(m_res*m_res)) + (v*m_res) + u]);}
		else {
			System.out.println("!!! kVecField.getAt("+u+","+v+","+w+") index out of range");
			return new kVec();
		}
	}
	public kVec[] getVecs(){
		return m_vecs;
	}
	public boolean setAt(int u, int v, int w, kVec vec){
		if (u < m_res && v < m_res && w < m_res ){m_vecs[(w*(m_res*m_res)) + (v*m_res) + u] = kVec.clone(vec); return true;}
		else {
			System.out.println("!!! kVecField.getAt("+u+","+v+","+w+") index out of range");
			return false;
		}
	}
	
	public kVec getNear(kVec pos){
		int[] add = getCellAddressNear(pos);
		if (add.length > 0){return getAt(add[0],add[1],add[2]);} 
		else {
			System.out.println("!!! kVecField.getNear failed");
			return new kVec();
		}
	}
	public kVec getCentroidNear(kVec pos){
		int[] add = getCellAddressNear(pos);
		if (add.length > 0){return getCellCentroidAt(add[0],add[1],add[2]);} 
		else {
			System.out.println("!!! kVecField.getNear failed");
			return new kVec();
		}
	}	
	
	public int[] getCellAddressNear(kVec pos){
		if (range.containsPoint(pos)){
			kVec vec = kVec.translate(pos, kVec.invert(range.minVec()) );
			int u = (int) (vec.x/range.deltX()*m_res);
			int v = (int) (vec.y/range.deltY()*m_res);
			int w = (int) (vec.z/range.deltZ()*m_res);
			return new int[]{u,v,w};
		} else {
			System.out.println("!!! kVecField.getCellAddressNear("+pos.x+","+pos.y+","+pos.z+") out of range");
			return new int[0];
		}
	}
	
	public kVec getCellCentroidAt(int u, int v, int w){
		//System.out.println("getCellCentroidAt("+u+","+v+","+w+")");
		if (u < m_res && v < m_res && w < m_res ) {
			kVec cell = cellDim();
			float x = ( ((float) u / (float) m_res) * range.deltX() )+range.minX()+(cell.x/2);
			float y = ( ((float) v / (float) m_res) * range.deltY() )+range.minY()+(cell.y/2);
			float z = ( ((float) w / (float) m_res) * range.deltZ() )+range.minZ()+(cell.z/2);
			//System.out.println("cent("+x+","+y+","+z+")");
			return new kVec(x,y,z);
		} else {
			System.out.println("!!! kVecField.getAt("+u+","+v+","+w+") index out of range");
			return new kVec();
		}
	}
	
	public kVec cellDim() { return new kVec( range.deltX()/m_res, range.deltY()/m_res , range.deltZ()/m_res );}
	
	/*
	 * BATCH MANIPULATION
	 * 
	 * 
	 * 
	 */	
	
	/**
	 * @param f0
	 * @param f1
	 * @param alpha
	 * @return a new vector field containing the interpolated vectors from f0 and f1
	 * AND THE kRANGE OF f0!!!
	 */
	public static kVecField interpolate(kVecField f0, kVecField f1, float alpha){
		if (f0.m_res == f1.m_res){
			kVecField ret = new kVecField(f0.range,f0.m_res);
			for (int n = 0; n < f0.m_vecs.length; n++) ret.m_vecs[n] = kVec.interpolate(f0.m_vecs[n], f1.m_vecs[n], alpha);
			return ret;
		} else {
			System.out.println("!!!!! kVecField.interpolate(): fields must have the same res. f0 has been returned");
			return f0;
		}
	}
	
	public void createVortex(kVec centerOfRotation, float range, float vecLength, boolean clockwise){
		float ang = (float)(Math.PI/2);
		if (clockwise){ang *= -1;}
		for (int u = 0; u < m_res; u++) 
			for (int v = 0; v < m_res; v++) 
				for (int w = 0; w < m_res; w++) {
					kVec pt = getCellCentroidAt(u,v,w);
					kVec vec = getAt(u,v,w);					
					centerOfRotation.y = pt.y;
					float dist = centerOfRotation.distance(pt);
					if (dist <= range){
						kVec rotVec = new kVec(pt,centerOfRotation);
						rotVec.resize(vecLength);
						rotVec.rotateAboutCardnialAxis(1,ang);
						float t = dist/range;
						setAt(u,v,w, kVec.interpolate(rotVec, vec, t) );
					}
				}
	}
	
	public void aimVecsTowardCenter(){
		kVec cent = range.center();
		aimVecsTowardPoint(cent);
	}
	public void aimVecsTowardPoint(kVec pt){
		for (int u = 0; u < m_res; u++) 
			for (int v = 0; v < m_res; v++) 
				for (int w = 0; w < m_res; w++) {
					float size = getAt(u,v,w).length();
					kVec vec = new kVec(getCellCentroidAt(u,v,w),pt);
					vec.resize(size);
					setAt(u, v, w, vec);
				}
	}
	
	public void rotateVecsAboutCardnialAxis(int axis , float radians){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].rotateAboutCardnialAxis( axis , radians);}
	public void rotateVecsAboutCardnialAxisDegrees( int axis , float degrees ){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].rotateAboutCardnialAxis( axis , degrees);}
	public void rotateVecsAboutCardnialAxis(int axis , float radians, kVec centerOfRotation){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].rotateAboutCardnialAxis( axis , radians, centerOfRotation);}
	public void rotateVecsAboutCardnialAxisDegrees( int axis , float degrees , kVec centerOfRotation){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].rotateAboutCardnialAxis( axis , degrees, centerOfRotation);}	
	public void resizeVecs(float size){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].resize(size);}
	public void normalizeVecs(){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].normalize();}
	public void invertVecs(){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].invert();}
	public void scaleVecs(float s){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].scale(s);}
	public void setVecsX(float x){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].x = x;}
	public void setVecsY(float y){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].y = y;}
	public void setVecsZ(float z){ for (int n = 0; n < m_vecs.length; n++) m_vecs[n].z = z;}
	
	/*
	 * SAMPLES
	 * 
	 * 
	 * 
	 */
	
	
	public static kVecField cylindricalField(kRange rng, int res, kVec centerOfRotation){
		kVecField vf = new kVecField(rng,res);
		vf.aimVecsTowardPoint(centerOfRotation);
		vf.setVecsY(0);
		vf.rotateVecsAboutCardnialAxis(1, (float)(Math.PI/2));
		return vf;
	}
	public static kVecField cylindricalField(kRange rng, int res){return cylindricalField(rng,res,new kVec(0,0,0));}
	
	public static kVecField helicalField(kRange rng, int res, float vertT, kVec centerOfRotation){
		kVecField vf = new kVecField(rng,res);
		vf.aimVecsTowardPoint(centerOfRotation);
		vf.setVecsY(vertT);
		vf.rotateVecsAboutCardnialAxis(1, (float)(Math.PI/2));
		return vf;
	}
	public static kVecField helicalField(kRange rng, int res, float vertT){return helicalField(rng,res,vertT,new kVec(0,0,0));}
	
}
